/**indexedDB数据库封装 */

class DataBase {
    constructor(name, version) {
        this.indexedDB =
            window.indexedDB ||
            window.mozIndexedDB ||
            window.webkitIndexedDB ||
            window.msIndexedDB;
        this.name = name;
        this.version = version || 1;
        this.db = null;
    }
}

/**
 * 公共封装
 */
DataBase.prototype.common = function () {
    const _this = this;
    const request = _this.indexedDB.open(_this.name, _this.version);
    return new Promise((resolve, reject) => {
        request.onerror = function () {
            reject(request);
        };
        request.onsuccess = function () {
            _this.db = this.result;
            resolve(_this.db);
        };
    });
};

/**创建对象库 */
DataBase.prototype.createStore = function (stores = []) {
    const _this = this;
    const request = _this.indexedDB.open(_this.name, _this.version);

    request.onerror = function () {
        console.log("request error: ", this.error);
    };

    request.onupgradeneeded = function () {
        _this.db = this.result;
        console.log("request upgraded, current version is: ", _this.db.version);
        stores.forEach((_item) => {
            const { storeName, keyPath, options } = _item;
            if (!_this.db.objectStoreNames.contains(storeName)) {
                const objectStore = _this.db.createObjectStore(storeName, {
                    keyPath,
                });
                if (options && Array.isArray(options)) {
                    options.forEach((item) => {
                        const { key, unique } = item;
                        if (key && unique !== undefined) {
                            objectStore.createIndex(key, key, { unique });
                        }
                    });
                }
            }
        });
    };
};

/**
 * 添加数据 支持一次性添加一条和多条
 * 如果data是object 就是添加一条
 * 如果data是array  就是添加多条
 */
DataBase.prototype.add = function (storeName, data) {
    this.common()
        .then((db) => {
            const addData = (_data) => {
                const transaction = db.transaction(storeName, "readwrite");
                const store = transaction.objectStore(storeName);
                const res = store.add(_data);
                res.onerror = function () {
                    console.log("add data error: ", res.error);
                };

                res.complete = function () {
                    console.log("add data success !");
                };
            };
            if (Object.prototype.toString.call(data) === "[object Object]") {
                addData(data);
            } else if (Array.isArray(data)) {
                for (let i = 0; i < data.length; i++) {
                    addData(data[i]);
                }
            }
        })
        .catch((err) => console.log(err.error));
};

/**删除数据 */
DataBase.prototype.delete = function (storeName, key) {
    this.common()
        .then((db) => {
            const transaction = db.transaction(storeName, "readwrite");
            const store = transaction.objectStore(storeName);
            const res = store.delete(key);
            res.onerror = function () {
                console.log("delete failed: ");
            };
        })
        .catch((err) => console.log(err.error));
};

/**
 * 更新/添加
 * 根据key判断，有就修改，没有就添加
 */
DataBase.prototype.put = function (storeName, data) {
    this.common()
        .then((db) => {
            const putData = (_data) => {
                const transaction = db.transaction(storeName, "readwrite");
                const store = transaction.objectStore(storeName);
                const res = store.put(_data);
                res.onerror = function () {
                    console.log("put failed: ", res.error);
                };
            };
            if (Object.prototype.toString.call(data) === "[object Object]") {
                putData(data);
            } else if (Array.isArray(data)) {
                for (let i = 0; i < data.length; i++) {
                    putData(data[i]);
                }
            }
        })
        .catch((err) => console.log(err.error));
};

/**
 * 查询一条数据
 * 这里可能需要拿到查询后的结果，所以需要返回有个promise，接收查询到的结果
 */
DataBase.prototype.find = function (storeName, key) {
    return new Promise((resolve) => {
        this.common()
            .then((db) => {
                const transaction = db.transaction(storeName, "readonly");
                const store = transaction.objectStore(storeName);
                const res = store.get(key);
                res.onerror = function () {
                    console.log("get failed: ", res.error);
                };
                res.onsuccess = function () {
                    resolve(res.result);
                };
            })
            .catch((err) => console.log(err.error));
    });
};

/**
 * 查询某个storeName中的所有数据,或者符合条件的数据N条数据
 */
DataBase.prototype.findAll = function (storeName, query, count) {
    return new Promise((resolve) => {
        this.common()
            .then((db) => {
                const transaction = db.transaction(storeName, "readonly");
                const store = transaction.objectStore(storeName);
                // 所有数据
                if (!query && !count) {
                    const res = store.getAll();
                    res.onerror = function () {
                        console.log("get all data failed: ", res.error);
                    };
                    res.onsuccess = function () {
                        resolve(res.result);
                    };
                    return;
                }
                // 所有数据中的前count条
                if (!query && count) {
                    const res = store.getAll();
                    res.onerror = function () {
                        console.log("get all data failed: ", res.error);
                    };
                    res.onsuccess = function () {
                        resolve(res.result.slice(0, count));
                    };
                    return;
                }
                // 符合条件的数据
                let condition;
                const { type, min, max } = query;
                switch (type) {
                    case ">=":
                        condition = IDBKeyRange.lowerBound(min);
                        break;
                    case ">":
                        condition = IDBKeyRange.lowerBound(min, true);
                        break;
                    case "<=":
                        condition = IDBKeyRange.upperBound(max);
                        break;
                    case "<":
                        condition = IDBKeyRange.upperBound(max, true);
                        break;
                    case ">=&<=":
                        condition = IDBKeyRange.bound(min, max);
                        break;
                    case ">&<":
                        condition = IDBKeyRange.bound(min, max, true, true);
                        break;
                    case ">&<=":
                        condition = IDBKeyRange.bound(min, max, true, false);
                        break;
                    case ">=&<":
                        condition = IDBKeyRange.bound(min, max, false, true);
                        break;
                    case "=":
                        condition = IDBKeyRange.only(min);
                        break;
                    default:
                        return;
                }
                const res = store.getAll(condition, count);
                res.onerror = function () {
                    console.log("get all data failed: ", res.error);
                };
                res.onsuccess = function () {
                    resolve(res.result.slice(0, count));
                };
            })
            .catch((err) => console.log(err.error));
    });
};

/**
 * 清除
 */
DataBase.prototype.clear = function (storeName) {
    this.common()
        .then((db) => {
            const transaction = db.transaction(storeName, "readwrite");
            const store = transaction.objectStore(storeName);
            const res = store.clear();
            res.onerror = function () {
                console.log("find error: ", res.error);
            };
        })
        .catch((err) => console.log(err.error));
};

export const openDB = (name, version) => {
    return new DataBase(name, version);
};
